Skip to content

Conversation

@serhalp
Copy link
Member

@serhalp serhalp commented Oct 28, 2025

Summary

This PR adds opt-in support for deploying React Router 7 apps to Netlify Edge Functions (Deno runtime) via the edge: boolean plugin option. Previously, the plugin only supported Netlify Functions (Node.js runtime).

Toward FRB-1519

Changes

  • Add edge?: boolean option to Vite plugin factory
  • When edge is enabled:
    • Configure Vite SSR build with target: 'webworker' and other Deno-compatible build config
    • Generate an edge function in .netlify/v1/edge-functions/, instead of a function .netlify/v1/functions/
  • Add excludedPaths?: string[] option to allow users to exclude paths from the React Router handler. Required when using edge: true and the project also has its own Netlify Functions with a configured path, otherwise only the RR handler runs on all dynamic paths.

Testing

  • Unskip the RR7 edge e2e suite
  • Add a few tests I noticed were missing for edge (or vice versa)
  • I thought I'd add tests to ensure prerendering works while I was here (this was added in RR7 but did not exist in Remix 2)... but unfortunately I discovered that this does not in fact work. I left the tests and marked them as failing.
  • You can also link this locally with https://github.com/netlify/react-router-template/ and confirm it works ✅

Documentation

I added complete documentation for opting in (and back out of) edge deployment.

Implementation

Edge support is more involved than regular serverless functions for three main reasons:

  1. EFs don't have a preferStatic option like NFs.
    • Instead, to avoid needlessly running compute to serve static assets, we must populate the EF's excludedPath with all published client assets. This skips the EF entirely for these paths.
  2. Although there is a precise processing order for EFs and for NFs, all EFs run before any NF.
    • This means that since our generated EF runs on path: '/*', if a user has their own NF with path: '/foo', our EF will run first and render a response from React Router and the user's NF will never run.
    • To that end, this PR adds and documents an excludedPaths?: [] option to the plugin.
    • There were various alternative solutions here, but this seemed like a reasonable approach for now.
  3. React Router 7 contains only a single default built-in server entry and this assumes a Node.js environment.
    • Unfortunately, this means when using edge: true the user must include their own app/entry.server.tsx that uses renderToReadableStream instead of renderToPipeableStream.
    • There may be some magic we can implement here to make this work out of the box. It didn't seem worth it to me to fight against the framework here. We can also always add this later.

@netlify
Copy link

netlify bot commented Oct 28, 2025

Deploy Preview for remix-edge ready!

Name Link
🔨 Latest commit f2fa84d
🔍 Latest deploy log https://app.netlify.com/projects/remix-edge/deploys/690b7fb266802600081ce233
😎 Deploy Preview https://deploy-preview-562--remix-edge.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Oct 28, 2025

Deploy Preview for remix-serverless ready!

Name Link
🔨 Latest commit f2fa84d
🔍 Latest deploy log https://app.netlify.com/projects/remix-serverless/deploys/690b7fb2069c55000868a3fd
😎 Deploy Preview https://deploy-preview-562--remix-serverless.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions bot added the type: feature code contributing to the implementation of a feature and/or user facing functionality label Oct 28, 2025
@serhalp serhalp force-pushed the serhalp/frb-1519-support-edge-rendering-with-react-router-7 branch from 5b9a00c to b552eb6 Compare October 29, 2025 00:11
@serhalp serhalp force-pushed the serhalp/frb-1519-support-edge-rendering-with-react-router-7 branch from 744b059 to 7fde98c Compare October 30, 2025 13:55
When the globally installed deno cli isn't a supported version, the build automatically tries to
install a local binary. Unfortunately when running multiple builds concurrently on the same machine
with default configuration, there's a race condition where a binary tries to get written to the same
path where one is currently running, leading to an OS error.

Just install the correct version globally.

See (internal link) https://netlify.slack.com/archives/C03ETTLQ9BP/p1761826639264259
it turns out...

> By default, tag-based purges apply to all of the site’s deploys. To target a specific deploy,
> specify one or more of the following [...]

from https://docs.netlify.com/build/caching/caching-overview/#use-a-function-with-the-purgecache-helper-to-purge-by-cache-tag

Whoops. So concurrent tests were conflicting with one another's expectations.
@serhalp serhalp force-pushed the serhalp/frb-1519-support-edge-rendering-with-react-router-7 branch from 7fde98c to 78d1afe Compare October 30, 2025 14:03
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File extracted as-is. Only change is the TNetlifyContext generic, so that NFs and EFs can pass in their own slightly different context types.

Copy link
Member Author

@serhalp serhalp Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of this file was extracted as is. The only change is the context generic and the runtimeName arg.

Comment on lines 20 to 24
"./function-handler": {
"types": "./dist/function-handler.d.mts",
"default": "./dist/function-handler.mjs"
},
"./edge-function-handler": {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the first lines of FUNCTION_HANDLER and EDGE_FUNCTION_HANDLER to understand what these are for. They're the runtime entry points.

(This should probably always have been an explicit separate export, honestly.)

> required for the Deno runtime. You may customize your server entry file, but see below for important edge runtime
> constraints.
```tsx
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole fixture is pretty much identical to the react-router-serverless-site one. I just removed durable, added app/entry.server.tsx, and passed in edge: true and excludedPaths in the vite config.

serhalp and others added 4 commits October 30, 2025 16:35
When a test retries, it reuses the deployed fixture and since the cache state is preserved from the
previous run, the initial state is incorrect.

Attaching a unique query string per run isolated each run.
@serhalp serhalp marked this pull request as ready for review October 30, 2025 20:53
@serhalp serhalp requested a review from a team as a code owner October 30, 2025 20:53
There appears to still be some flakiness that I cannot reproduce when running `.only` on a test,
which implies that there's somethhing conflicting somehow across tests running concurrently.
@serhalp serhalp force-pushed the serhalp/frb-1519-support-edge-rendering-with-react-router-7 branch from fafc121 to c6d75cb Compare October 31, 2025 20:39
By declaring the ESM entries together, we allow code splitting, which
keeps the size of our package to a minimum.
This reverts commit 8f54892.

It turns out it's the docs that are wrong :(
D'oh. The cache purge calls in the e2e tests were 404ing...
Turns out we had the same problem here as well.
@serhalp serhalp force-pushed the serhalp/frb-1519-support-edge-rendering-with-react-router-7 branch from df6e147 to 3beb85c Compare November 4, 2025 00:10
@serhalp serhalp requested a review from pieh November 5, 2025 16:56
@serhalp serhalp merged commit bbcb41e into main Nov 5, 2025
17 checks passed
@serhalp serhalp deleted the serhalp/frb-1519-support-edge-rendering-with-react-router-7 branch November 5, 2025 17:17
@token-generator-app token-generator-app bot mentioned this pull request Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature code contributing to the implementation of a feature and/or user facing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants